import selectors
import socket
import threading
import random
import os

from FPDLCore.BaseServerNet import BaseServerNet
from FPDLCore.DownloadWorkerServer import DownloadWorkerServer
from FPDLCore import MessageDefs


class EntryServer(BaseServerNet):
    def __init__(self, port, maxworker=None):
        BaseServerNet.__init__(self, port)
        self.max_worker = maxworker is None and 10 or maxworker
        self.workerlist = {}
        self.workerportsused = {}
        self.s_data = {}
        self.r_data = {}

    def onstart(self):
        self.startworker()

    def onaccepted(self, conn):
        self.waitread(conn)

    def onread(self, conn, data):
        if conn in self.r_data:
            data = self.r_data[conn] + data
        request = MessageDefs.deserialize(data)
        if request:
            if conn in self.r_data:
                del self.r_data[conn]
            worker = self.getworker()
            if worker:
                errormsg = None
                try:
                    filesize = os.stat(request.filepath).st_size
                except FileNotFoundError:
                    filesize = 0
                    errormsg = 'file not found'
                respond = MessageDefs.WorkerInfoRespond(worker.port, filesize, errormsg)
                sdata = MessageDefs.serialize(respond)
                self.s_data[conn] = sdata
                self.waitsend(conn)
            else:
                print("Fail to get idle worker! Close connection:", conn)
                self.close(conn)
        else:
            self.r_data[conn] = data


    def onsend(self, conn):
        if conn in self.s_data:
            data = self.s_data[conn]
        else:
            data = None
        if data:
            sended = conn.send(data)
            if sended < len(data):
                self.s_data[conn] = data[sended:]
            else:
                if conn in self.s_data:
                    del self.s_data[conn]
                self.close(conn)
        else:
            self.close(conn)

    def onclose(self, conn):
        pass

    def startworker(self):
        count = self.max_worker - len(self.workerlist)

        self.workerportsused.clear()
        for k, v in self.workerlist.items():
            self.workerportsused[v['worker'].port] = True

        def target(port):
            worker = DownloadWorkerServer(self, port)
            worker.start()

        random.seed()
        for i in range(count):
            workerport = None
            while True:
                workerport = random.randint(6000, 65534)
                if workerport not in self.workerportsused.keys():
                    self.workerportsused[workerport] = True
                    break
            trd = threading.Thread(target=target, args=(workerport,), daemon=False)
            trd.start()

    def registerworker(self, worker):
        self.workerlist[worker.port] = {
            'worker': worker
        }

    def unregisterworker(self, worker):
        if worker.port in self.workerlist:
            if worker.port in self.workerportsused:
                del self.workerportsused[worker.port]
            del self.workerlist[worker.port]

    def getworker(self):
        self.startenoughworker()

        min = 99999
        worker = None
        for k, v in self.workerlist.items():
            if v['worker'].workload < min:
                worker = v['worker']
                min = worker.workload

        if not worker is None:
            worker.workload += 1
        return worker

    def startenoughworker(self):
        if len(self.workerlist) < self.max_worker:
            self.startworker()
